package com.zl.registration.service.impl;

import com.zl.registration.module.prefix.KeyPrefix;
import com.zl.registration.service.RedisService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;

import javax.annotation.Resource;
import java.util.Collections;

/**
 * @author zoulei
 * @date 2021/11/23 11:55
 */
@Service
public class RedisServiceImpl implements RedisService {

    private static final Logger  logger = LoggerFactory.getLogger(RedisServiceImpl.class);

    private static final String LOCK_SUCCESS = "OK";

    private static final Long RELEASE_SUCCESS = 1L;

    @Resource
    private JedisPool jedisPool;

    @Override
    public String get(KeyPrefix prefix, String key) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String realKey = prefix.getPrefix() + key;
            return jedis.get(realKey);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
        return null;
    }

    @Override
    public void set(KeyPrefix prefix, String key, String val) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (prefix.expireSeconds() != 0) {
                SetParams px = new SetParams().nx().px(prefix.expireSeconds());
                String realKey = prefix.getPrefix() + key;
                jedis.set(realKey, val, px);
            } else {
                String realKey = prefix.getPrefix() + key;
                jedis.set(realKey, val);
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
    }

    @Override
    public boolean exists(KeyPrefix keyPrefix, String key) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String realKey = keyPrefix.getPrefix() + key;
            return jedis.exists(realKey);
        } finally {
            close(jedis);
        }
    }

    @Override
    public long incr(KeyPrefix keyPrefix, String key) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String realKey = keyPrefix.getPrefix() + key;
            return jedis.incr(realKey);
        } finally {
            close(jedis);
        }
    }

    @Override
    public long decr(KeyPrefix keyPrefix, String key) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String realKey = keyPrefix.getPrefix() + key;
            return jedis.decr(realKey);
        } finally {
            close(jedis);
        }
    }

    @Override
    public boolean delete(KeyPrefix prefix, String key) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String realKey = prefix.getPrefix() + key;
            Long del = jedis.del(realKey);
            return del > 0;
        } finally {
            close(jedis);
        }
    }

    @Override
    public boolean lock(String lockKey, String uniqueValue, int expireTime) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            // 获取锁
            SetParams px = new SetParams().nx().px(expireTime);
            String result = jedis.set(lockKey, uniqueValue, px);

            return LOCK_SUCCESS.equals(result);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
        return false;
    }

    @Override
    public boolean unlock(String lockKey, String uniqueValue) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            // 使用Lua脚本保证操作的原子性
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                    "return redis.call('del', KEYS[1]) " +
                    "else return 0 " +
                    "end";
            Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(uniqueValue));
            return RELEASE_SUCCESS.equals(result);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            close(jedis);
        }
        return false;
    }

    private static void close(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }
}
